#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "../dcmtk_include.h"

#include <limits.h>
#include <QDebug>
#include <QTextEdit>
#include <QFile>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;

template<typename T>
void CreateShowImage(T* data,int row,int col,short winLevel,short winWindow,
                     unsigned char*& resData){
    short minValue = winLevel-winWindow/2;
    short maxValue = winLevel+winWindow/2;
    for(int i=0;i<row*col;i++){
        T tmpValue = data[i];
        if((tmpValue < minValue)){
            resData[i] = 0;
        }else if((tmpValue > maxValue)){
            resData[i] = 255;
        }else {
            float tmpRatio = float(tmpValue-minValue)*255.0/float(winWindow);
            if(tmpRatio>255.0){
                tmpRatio = 255;
            }
            resData[i] = (unsigned char)tmpRatio;
        }
    }
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    OFString filename = "./data/ori.dcm";
    DicomImage *image = new DicomImage(filename.c_str());
    if (image != NULL)
    {
        qDebug() << image->getStatus();
      if (image->getStatus() == EIS_Normal)
      {
          if (image->isMonochrome()){//是否为单色图像
            image->setMinMaxWindow();
          }
        Uint8 *pixelData = (Uint8 *)(image->getOutputData(8 /* bits per sample */));
        if (pixelData != NULL)
        {
          /* do something useful with the pixel data */
        }
      } else{
        cerr << "Error: cannot load DICOM image (" << DicomImage::getString(image->getStatus()) << ")" << endl;
      }
    }
    delete image;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::CalDcmItemSize()
{
    OFFilename filename = "./data/ori.dcm";
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
        DcmMetaInfo *metalnfo = fileformat.getMetaInfo();
        DcmDataset *dataset = fileformat.getDataset();

        DcmObject* datasetItem = dataset->nextInContainer(nullptr);
        Uint32 allitemlength = 0;
        while (datasetItem)
        {
            cout << " (" << hex << setw(4) << setfill('0') << datasetItem->getGTag();
            cout << "," << setw(4) << setfill('0') << datasetItem->getETag() << ")" << endl;
            allitemlength += datasetItem->getLength();
            datasetItem = dataset->nextInContainer(datasetItem);
        }

        qDebug() << "allitemlength:" << allitemlength;
        DcmObject* metaitem = metalnfo->nextInContainer(nullptr);
        while (metaitem)
        {
             qDebug() << "tag:"<< metaitem->getTag().toString().data();
             qDebug() << "NumberOfValues:"<< metaitem->getNumberOfValues();
             qDebug() << "vr name:"<< metaitem->getVR();
             qDebug() << "length:"<< metaitem->getLength();
             allitemlength += datasetItem->getLength();
             metaitem = metalnfo->nextInContainer(metaitem);
        }
        qDebug() << "allitemlength:" << allitemlength;
    }
}

bool MainWindow::SafeChooseRepresentation(DcmDataset* dcmData, int xfer,int quality){

    DcmDataset *pDS = (DcmDataset*)dcmData;

    DJ_RPLossy		rp_lossy( quality );
    const DcmRepresentationParameter *rp = &rp_lossy;

    OFCondition		cond = EC_Normal;

    if(xfer == EXS_JPEG2000LosslessOnly)
    {
        DJ_RPLossy  rpLossy(100);
        cond = pDS->chooseRepresentation( (E_TransferSyntax)xfer, &rpLossy );
    }
    else if( xfer == EXS_JPEG2000)
    {
        DJ_RPLossy  rpLossy(quality);
         cond = pDS->chooseRepresentation( (E_TransferSyntax)xfer, &rpLossy );
    }
    else
    {
         cond = pDS->chooseRepresentation( (E_TransferSyntax)xfer, rp );
         qDebug() << cond.good();
    }

    return !cond.bad();
}

bool MainWindow::SaveDataset(DcmDataset *pDS, quint8 eXfer,
                             long lTempSize, QByteArray &aBuffer)
{
    void*	pBuffer = NULL;
    long	lBufferSize = 0;

    bool bResult = SaveDataset( pDS, eXfer, lTempSize, pBuffer, lBufferSize );

    if( bResult )
    {
        aBuffer.clear();
        aBuffer.append( (char*)pBuffer, lBufferSize );
    }

    if( pBuffer != NULL )
    {
        delete[] (char*)pBuffer;
    }

    return bResult;
}

bool MainWindow::SaveDataset(DcmDataset *pDS, quint8 eXfer, long lTempSize,
                             void *&pBuffer, long &lBufferSize)
{
    if( pDS == NULL ){
        return false;
    }

    pDS->findAndDeleteElement( DCM_SourceImageSequence );
    pDS->findAndDeleteElement( DCM_DerivationCodeSequence );

    E_TransferSyntax xfer = E_TransferSyntax( eXfer );

    long lTempBufferSize = lTempSize;
    qDebug() << "lTempBufferSize:" << lTempBufferSize;
    qint8* pTempBuffer = new qint8[lTempBufferSize];

    OFCondition l_error = EC_InvalidFilename;

    DcmWriteCache wcache;
    /* open file for output */
    DcmOutputBufferStream bufferStream( pTempBuffer, lTempBufferSize );

    DcmFileFormat dcmFile( pDS );
    dcmFile.transferInit();
    //	l_error = dcmFile.saveFile( "E:/Temp/ssss.dcm" );
    l_error = dcmFile.write(bufferStream, xfer, EET_ExplicitLength, &wcache );
    //l_error = pDS->write(bufferStream, xfer, EET_UndefinedLength, &wcache );
    dcmFile.transferEnd();

    // 导出有效内存
    long lNewBufferSize = bufferStream.tell();
    qDebug() << "lNewBufferSize:" << lNewBufferSize;
    qint8* pNewBuffer = new qint8[lNewBufferSize];

    memcpy( pNewBuffer, pTempBuffer, lNewBufferSize );
    delete[] pTempBuffer;

    pBuffer = pNewBuffer;
    lBufferSize	= lNewBufferSize;
    return true;
}

bool MainWindow::SaveEmptyDicom()
{

    char uid[100];
    DcmFileFormat fileformat;
    DcmDataset *dataset = fileformat.getDataset();
//    dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);
//    dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
//    dataset->putAndInsertString(DCM_PatientName, "Doe^John");
//    OFCondition status = fileformat22.saveFile("H:/DCMWorklist/worklist/test.wl", EXS_LittleEndianExplicit);

    dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);
    dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
    dataset->putAndInsertString(DCM_ImplementationVersionName,"OFFIS_DCMTK_361");
    dataset->putAndInsertString(DCM_SpecificCharacterSet,"");
    dataset->putAndInsertString(DCM_PatientName, "");
    dataset->putAndInsertString(DCM_PatientID,"123456");
    dataset->putAndInsertString(DCM_PatientBirthDate,"");
    dataset->putAndInsertString(DCM_PatientSex,"");
    OFCondition status = fileformat.saveFile("H:/DCMWorklist/worklist/testqry.wl", EXS_LittleEndianExplicit);
    if (status.bad())
        cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;
    qDebug() << "end write";
}


#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"
#include "dcmtk/dcmdata/dcistrmf.h"
#include "dcmtk/dcmdata/libi2d/i2dbmps.h"
#include <direct.h>
void AddDicomElements(DcmDataset*& dataset)
{
    //构建测试数据
    /*	添加患者信息	*/
    dataset->putAndInsertUint16(DCM_AccessionNumber,0);
    dataset->putAndInsertString(DCM_PatientName,"zssure",true);
    dataset->putAndInsertString(DCM_PatientID,"2234");
    dataset->putAndInsertString(DCM_PatientBirthDate,"20141221");
    dataset->putAndInsertString(DCM_PatientSex,"M");

    /*	添加Study信息	*/
    dataset->putAndInsertString(DCM_StudyDate,"20141221");
    dataset->putAndInsertString(DCM_StudyTime,"195411");
    char uid[100];
    dcmGenerateUniqueIdentifier(uid,SITE_STUDY_UID_ROOT);
    dataset->putAndInsertString(DCM_StudyInstanceUID,uid);
    dataset->putAndInsertString(DCM_StudyID,"1111");


    /*	添加Series信息	*/
    dataset->putAndInsertString(DCM_SeriesDate,"20141221");
    dataset->putAndInsertString(DCM_SeriesTime,"195411");
    memset(uid,0,sizeof(char)*100);
    dcmGenerateUniqueIdentifier(uid,SITE_SERIES_UID_ROOT);
    dataset->putAndInsertString(DCM_SeriesInstanceUID,uid);
    /*	添加Image信息	*/
    dataset->putAndInsertString(DCM_ImageType,"ORIGINAL\\PRIMARY\\AXIAL");
    dataset->putAndInsertString(DCM_ContentDate,"20141221");
    dataset->putAndInsertString(DCM_ContentTime,"200700");
    dataset->putAndInsertString(DCM_InstanceNumber,"1");
    dataset->putAndInsertString(DCM_SamplesPerPixel,"1");
    dataset->putAndInsertString(DCM_PhotometricInterpretation,"MONOCHROME2");
    dataset->putAndInsertString(DCM_PixelSpacing,"0.3\\0.3");
    dataset->putAndInsertString(DCM_BitsAllocated,"16");
    dataset->putAndInsertString(DCM_BitsStored,"16");
    dataset->putAndInsertString(DCM_HighBit,"15");
    dataset->putAndInsertString(DCM_WindowCenter,"600");
    dataset->putAndInsertString(DCM_WindowWidth,"800");
    dataset->putAndInsertString(DCM_RescaleIntercept,"0");
    dataset->putAndInsertString(DCM_RescaleSlope,"1");
}
bool MainWindow::SaveDcmToBMP()
{
    OFCondition status;

    DcmFileFormat fileformat;
    DcmDataset* mydatasete=fileformat.getDataset();
    AddDicomElements((DcmDataset*&)mydatasete);
    Uint16 rows,cols,samplePerPixel,bitsAlloc,bitsStored,highBit,
            pixelRpr,planConf,pixAspectH,pixAspectV;
    OFString photoMetrInt;
    Uint32 length;
    E_TransferSyntax ts;
    char* mydata=new char[1024*1024*10];
    memset(mydata,0,sizeof(char)*1024*1024*10);
    char* tmpData=mydata;
    char curDir[100];
    getcwd(curDir,100);
    //add bmp data
    for(int i=0;i<4;++i)
    {
        OFString num;
        char numtmp[100];
        memset(numtmp,0,sizeof(char)*100);
        sprintf(numtmp,"%s\\data\\bmp\\%d.bmp",curDir,i+1);
        OFString filename=OFString(numtmp);
        I2DBmpSource* bmpSource=new I2DBmpSource();
        bmpSource->setImageFile(filename);

        char* pixData=NULL;
        bmpSource->readPixelData(rows,cols,samplePerPixel,photoMetrInt,bitsAlloc,
                                 bitsStored,highBit,pixelRpr,planConf,pixAspectH,
                                 pixAspectV,pixData,length,ts);
        memcpy(tmpData,pixData,length);
        tmpData+=length;

        delete bmpSource;
    };

    mydatasete->putAndInsertUint16(DCM_SamplesPerPixel,samplePerPixel);
    mydatasete->putAndInsertString(DCM_NumberOfFrames,"4");
    mydatasete->putAndInsertUint16(DCM_Rows,rows);
    mydatasete->putAndInsertUint16(DCM_Columns,cols);
    mydatasete->putAndInsertUint16(DCM_BitsAllocated,bitsAlloc);
    mydatasete->putAndInsertUint16(DCM_BitsStored,bitsStored);
    mydatasete->putAndInsertUint16(DCM_HighBit,highBit);
    mydatasete->putAndInsertUint8Array(DCM_PixelData,(Uint8*)mydata,4*length);
    mydatasete->putAndInsertOFStringArray(DCM_PhotometricInterpretation,photoMetrInt);
    //mydatasete->putAndInsertString(DCM_PlanarConfiguration,"1");
    status=fileformat.saveFile("./data/bmp/Multibmp2dcmtest.dcm",ts);
    if(status.bad())
    {
        std::cout<<"Error:("<<status.text()<<")\n";
    }

    /**
    OFFilename filename = "./data/ori.dcm";
    DJEncoderRegistration::registerCodecs(); // register JPEG codecs
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
      DcmDataset *dataset = fileformat.getDataset();
      E_TransferSyntax xfer = dataset->getOriginalXfer();

      dataset->chooseRepresentation(EXS_LittleEndianExplicit,NULL);
      DicomImage *pdcmImage=new DicomImage(&fileformat,EXS_LittleEndianExplicit,
                                           CIF_AcrNemaCompatibility,0,1);
      if(pdcmImage->getStatus()!=EIS_Normal){
          qDebug() << "exit:" << pdcmImage->getStatus();
          exit(0);
      }
      void* pDIB=NULL;
      int size=0;
      pdcmImage->createWindowsDIB(pDIB,size,0,8,1,1);

      RGBQUAD* mPalette=new RGBQUAD[256];
      memset(mPalette,0,sizeof(RGBQUAD)*256);
      for(int i=0;i<256;++i){
          mPalette[i].rgbBlue=i;
          mPalette[i].rgbGreen=i;
          mPalette[i].rgbRed=i;
      }

    }

    DJEncoderRegistration::cleanup(); // deregister JPEG codecs*/
    qDebug() << "SaveDcmToBMP End!";
    return true;
}

void MainWindow::ChangeDcmItemAndSave()
{
    OFFilename filename = "./data/ori.dcm";
    DJEncoderRegistration::registerCodecs(); // register JPEG codecs
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
        //get pixel data
        DcmDataset *dataset = fileformat.getDataset();
        // decompress data set if compressed
        //dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
        DcmElement* element=NULL;
        dataset->findAndGetElement(DCM_PixelData,element);
        Uint16* pImage=NULL;
        element->getUint16Array(pImage);
        //end
        unsigned short* pPixelData = static_cast<unsigned short*>(pImage);

        OFString WindowCenter,WindowWidth,Rows,Columns;
        dataset->findAndGetOFString(DCM_WindowCenter, WindowCenter);
        dataset->findAndGetOFString(DCM_WindowWidth, WindowWidth);
        dataset->findAndGetOFString(DCM_Rows, Rows);
        dataset->findAndGetOFString(DCM_Columns, Columns);
        int nRows = QString(Rows.data()).toInt();
        int nColumns = QString(Columns.data()).toInt();

        int size=nColumns*nRows;
        for(int i=0;i<size;++i)
            if(pPixelData[i]<1800)
                pPixelData[i]=0;
            else
                pPixelData[i]=2000;
        UINT16 *pData=new UINT16[size];
        memcpy(pData,pPixelData,size*sizeof(UINT16));
//        DcmMetaInfo *metainfo=fileformat.getMetaInfo();
//        metainfo->remove(DCM_TransferSyntaxUID);
//        delete dataset->remove(DCM_PatientName);
//        dataset->putAndInsertString(DCM_PatientName,"TEST-THU");
        delete dataset->remove(DCM_PixelData);
        dataset->putAndInsertUint16Array(DCM_PixelData,pData,size);
        fileformat.saveFile("./data/ChangeDcmItem.dcm",EXS_LittleEndianExplicit);
    }
    std::cout << "ChangeDcmItemAndSave End!" << std::endl;
}

void MainWindow::on_dcmjpeg_compress_clicked()
{
    OFFilename filename = "./data/ori.dcm";
    DJEncoderRegistration::registerCodecs(); // register JPEG codecs
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
      DcmDataset *dataset = fileformat.getDataset();

      DcmItem *metaInfo = fileformat.getMetaInfo();
      DJ_RPLossless params; // codec parameters, we use the defaults
      // this causes the lossless JPEG version of the dataset to be created
      if (dataset->chooseRepresentation(EXS_JPEGProcess14SV1, &params).good() &&
          dataset->canWriteXfer(EXS_JPEGProcess14SV1))
      {
        // force the meta-header UIDs to be re-generated when storing the file
        // since the UIDs in the data set may have changed
        delete metaInfo->remove(DCM_MediaStorageSOPClassUID);
        delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID);
        // store in lossless JPEG format
          OFFilename compressfilename = "./data/ori_jpeg_compress.dcm";
        fileformat.saveFile(compressfilename, EXS_JPEGProcess14SV1);
      }
    }
    DJEncoderRegistration::cleanup(); // deregister JPEG codecs
    qDebug() << "on_dcmjpeg_compress_clicked End!";
}

void MainWindow::on_dcmjpeg_decompress_clicked()
{
    DJDecoderRegistration::registerCodecs(); // register JPEG codecs
    DcmFileFormat fileformat;
    OFFilename compressfilename = "./data/xxg.dcm";
    if (fileformat.loadFile(compressfilename).good())
    {
      DcmDataset *dataset = fileformat.getDataset();
      // decompress data set if compressed
      if (dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL).good() &&
          dataset->canWriteXfer(EXS_LittleEndianExplicit))
      {
          OFFilename filename = "./data/xxg_decompressed.dcm";
          fileformat.saveFile(filename, EXS_LittleEndianExplicit);
      }
    }
    DJDecoderRegistration::cleanup(); // deregister JPEG codecs
    qDebug() << "on_dcmjpeg_decompress_clicked End!";
}

void MainWindow::on_ShowDicom_clicked()
{
    OFFilename filename = "./data/ori.dcm";
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
        DcmDataset *dataset = fileformat.getDataset();

        OFString WindowCenter,WindowWidth,Rows,Columns;
        dataset->findAndGetOFString(DCM_WindowCenter, WindowCenter);
        dataset->findAndGetOFString(DCM_WindowWidth, WindowWidth);
        dataset->findAndGetOFString(DCM_Rows, Rows);
        dataset->findAndGetOFString(DCM_Columns, Columns);
        int nWindowCenter = QString(WindowCenter.data()).toInt();
        int nWindowWidth = QString(WindowWidth.data()).toInt();
        int nRows = QString(Rows.data()).toInt();
        int nColumns = QString(Columns.data()).toInt();
        std::cout<<nWindowCenter<<" "<<nWindowWidth<<std::endl;
        std::cout<<nRows<<" "<<nColumns<<std::endl;

        OFString BitsAllocated,PixelRepresentation,RescaleIntercept;
        dataset->findAndGetOFString(DCM_BitsAllocated, BitsAllocated);
        dataset->findAndGetOFString(DCM_PixelRepresentation, PixelRepresentation);
        dataset->findAndGetOFString(DCM_RescaleIntercept, RescaleIntercept);
        short nBitsAllocated = QString(BitsAllocated.data()).toShort();
        short nPixelRepresentation = QString(PixelRepresentation.data()).toShort();
        short nRescaleIntercept = QString(RescaleIntercept.data()).toShort();
        std::cout<<nBitsAllocated << " " << nPixelRepresentation
                << " " << nRescaleIntercept <<std::endl;

        DcmElement *element = NULL;
        OFCondition result = dataset->findAndGetElement(
                    DCM_PixelData, element);
        if(result.bad()){
          std::cout <<"pixelElement:" << result.text() << std::endl;
          return;
        }

        Uint16* pixData16;
        result = element->getUint16Array(pixData16);
        if (result.bad()){
          std::cout <<"pixelData:"<< result.text() << std::endl;
          return;
        }

        if(nBitsAllocated == 16){
            if(nPixelRepresentation == 0){//unsigned
                Sint16* realData = (Sint16*)pixData16;
                for(int i=0;i<nRows*nColumns;i++){
                    realData[i] = realData[i]+nRescaleIntercept;
                }
                unsigned char* showData = new unsigned char[nRows*nColumns];
                CreateShowImage(realData,nRows,nColumns,
                                nWindowCenter,nWindowWidth,showData);
                cv::Mat imag = cv::Mat(nRows, nColumns, CV_8U, showData);
                cv::imshow("image", imag);
                cv::waitKey();
            }else if(nPixelRepresentation == 1){//signed
            }
        }
    }
}

void MainWindow::on_DecodeBase64_clicked()
{
    int nRows = ui->lineEdit_Rows->text().toInt();
    int nColumns = ui->lineEdit_Columns->text().toInt();
    QString base64str = ui->textEdit->toPlainText();
    QByteArray baBase64 = QByteArray::fromBase64(base64str.toUtf8());
    QByteArray baDest = qUncompress((uchar*)(baBase64.data()), baBase64.size());

    qDebug() << baDest.size();
    quint8* data = new quint8[baDest.size()];
    memcpy(data, baDest.data(), baDest.size());
    for(int i=0;i<nRows*nColumns;i++){
        if(data[i]>0){
            data[i] = 200;
        }
    }

    cv::Mat imag = cv::Mat(nRows, nColumns, CV_8U, data);
    cv::imshow("image", imag);
    cv::waitKey();
}

void MainWindow::on_ChangeDcmItemAndSaveBtn_clicked()
{
    ChangeDcmItemAndSave();
}

void MainWindow::on_SaveDcmBmpBtn_clicked()
{
    SaveDcmToBMP();
}

void MainWindow::on_ConvertDcm2BytearrBtn_clicked()
{
    OFFilename filename = "./data/ori.dcm";
    DJEncoderRegistration::registerCodecs(); // register JPEG codecs
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filename).good()){
        DcmDataset *dataset = fileformat.getDataset();
        bool res = SafeChooseRepresentation(dataset,EXS_JPEGProcess2_4,90);
        qDebug() << "SafeChooseRepresentation:" << res;
        if(res){
            QByteArray byreArr;
            res = SaveDataset(dataset,EXS_JPEGProcess2_4,2*1024*1024,byreArr);
            qDebug() << "SaveDataset:" << res;
            QFile saveFile("./data/bytearr.dcm");
            if(!saveFile.open(QFile::WriteOnly)){
                qDebug() << "open failed!";
                return;
            }
            saveFile.resize(0);
            qint64 nRet = saveFile.write(byreArr.data(),byreArr.size());
            qDebug() << "nRet:" << nRet;
            saveFile.close();
        }
    }
}

void MainWindow::on_CalDcmItemSizeBtn_clicked()
{
    CalDcmItemSize();
}
